package com.demo2do.wechatmcs.handle.impl;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.demo2do.core.entity.result.HttpResult;
import com.demo2do.core.http.annotation.Rao;
import com.demo2do.core.utils.StringUtils;
import com.demo2do.core.web.utils.WebUtils;
import com.demo2do.core.wechat.Wechat;
import com.demo2do.core.wechat.rao.wechat.WebAuthRao;
import com.demo2do.core.wechat.utils.WechatUtils;
import com.demo2do.wechatmcs.entity.AuthScope;
import com.demo2do.wechatmcs.entity.webauth.WebAuth;
import com.demo2do.wechatmcs.entity.webauth.WebAuthClient;
import com.demo2do.wechatmcs.entity.webauth.WebAuthConfig;
import com.demo2do.wechatmcs.entity.webauth.WebAuthToken;
import com.demo2do.wechatmcs.entity.webauth.WechatAccount;
import com.demo2do.wechatmcs.handle.WebAuthHandler;
import com.demo2do.wechatmcs.service.WebAuthService;

/**
 * 
 * @author mohanlan
 *
 */
@Component("webAuthHandler")
public class WebAuthhandlerImpl implements WebAuthHandler {
    
    private static final Logger logger = LoggerFactory.getLogger(WebAuthHandler.class);
    
    @Value("#{wechat['wechat.appid']}")
    private String appid;
    
    @Value("#{wechat['wechat.appsecret']}")
    private String appsecret;
    
    @Value("#{ctx['ctx.domain']}")
    private String domain;
    
    @Rao
    private WebAuthRao webAuthRao;
    
    @Autowired
    private WebAuthService webAuthService;

    /* (non-Javadoc)
     * @see com.demo2do.wechatmcs.handle.WebAuthHandler#validate(com.demo2do.wechatmcs.entity.webauth.WebAuth, com.demo2do.wechatmcs.entity.webauth.WebAuthClient)
     */
    public boolean validate(WebAuth webAuth, WebAuthClient authClient) {
        
        // 1. check web_auth_client
        if(authClient != null && authClient.isValid()) {
            
            // 2. check scope input
            if(AuthScope.isWebAuth(webAuth.getScope())) {
                
                return true;
                
            }
            
        }
        
        return false;
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.wechatmcs.handle.WebAuthHandler#prepare(com.demo2do.wechatmcs.entity.webauth.WebAuth, com.demo2do.wechatmcs.entity.webauth.WebAuthConfig)
     */
    public String prepare(WebAuth webAuth, WebAuthConfig config) {
        
        // 1. save web auth request
        webAuthService.createWebAuth(webAuth, config);
        
        // 2. generate code url
        String redirectUrl = StringUtils.connect(this.domain, "/wechat/web-auth/callback");
        
        return StringUtils.connect(Wechat.WebAuthUrl.CODE, 
                                   "?appid=", appid, 
                                   "&redirect_uri=", WebUtils.encode(redirectUrl, "UTF-8"), 
                                   "&response_type=code", 
                                   "&scope=", webAuth.getScope(),
                                   "&state=", webAuth.getState(),
                                   "#wechat_redirect");
    
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.wechatmcs.handle.WebAuthHandler#validate(java.lang.String)
     */
    public boolean validate(String state) {
        
        WebAuth webAuth = webAuthService.getWebAuth(state);
        
        if(webAuth != null && !webAuth.isVisited()) {
            return true;
        }
        
        logger.error("#process() - web auth is a out-of-date or visited request");
        
        return false;
    }
    

    /*
     * (non-Javadoc)
     * @see com.demo2do.wechatmcs.handle.WebAuthHandler#process(java.lang.String, java.lang.String)
     */
    public String process(String code, String state) {
        
        // 0. get web_auth and web_auth_config
        WebAuth webAuth = webAuthService.getWebAuth(state);
        
        WebAuthClient client = webAuthService.getWebAuthClient(webAuth.getClient());
        WebAuthConfig config = new WebAuthConfig(client);
        
        try {
            
            // 1. call rao to get web auth token and valid
            WebAuthToken webAuthToken = this.getWebAuthToken(code, state);
            
            // 2. authorize this web_auth by write back openid
            webAuthService.authorize(webAuth, webAuthToken.getOpenid());
            
            // 3. refresh wechat account information when scope requires  
            if (AuthScope.isUserInfo(webAuth.getAuthScope())) { // retrieve wechat account from tencent
                
                String accessToken = webAuthToken.getAccessToken();
                String openid = webAuthToken.getOpenid();
                
                logger.info("#process - get wechat account using accessToken[{}] and openid[{}]", accessToken, openid);
                
                // 4. get wechat account
                WechatAccount wechatAccount = this.getWechatAccount(accessToken, openid);
                
                // 5. refresh wechat account
                webAuthService.refresh(webAuth, wechatAccount);
                
            } 
            
            return StringUtils.connect(config.getCallback(), "?uuid=", webAuth.getId());
            
        } catch (Exception e) {
            
            logger.error("#process() - faild", e);
            
            return config.getErrorBack();
        
        }
        
    }

    /**
     * Get web auth token
     * 
     * @param code
     * @param state
     * @return
     */
    private WebAuthToken getWebAuthToken(String code, String state) {
        
        logger.trace("#getWebAuthToken - Start get web auth token with params appid[{}], ", this.appid, this.appsecret);
        
        // 1. call rao to get web auth token
        HttpResult httpResult = webAuthRao.getAccessToken(appid, appsecret, code);
        
        if (WechatUtils.isValid(httpResult)) {
            
            // 2. get response date
            Map<String, Object> response = httpResult.describe();
            
            String accessToken = (String) response.get("access_token");
            String refreshToken = (String) response.get("refresh_token");
            String openid = (String) response.get("openid");
            String scope = (String) response.get("scope");
            
            return new WebAuthToken(accessToken, refreshToken, openid, scope);
        }
        
        throw WechatUtils.toBusinessException(httpResult);
    }

    /**
     * Get wechat account
     * 
     * @param accessToken
     * @param openid
     * @return
     */
    protected WechatAccount getWechatAccount(String accessToken, String openid) {
        
        logger.trace("#getWechatAccount - Start get wechat account with params appid[{}], ", this.appid, this.appsecret);
        
        HttpResult httpResult = webAuthRao.getUserInfo(accessToken, openid);
        
        if (WechatUtils.isValid(httpResult)) {
            
            // 2. get response date
            Map<String, Object> response = httpResult.describe();
            
            return new WechatAccount(response);
        }
        
        throw WechatUtils.toBusinessException(httpResult);
    }

}
